﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

public static class IEnumerableExtensions
{
    /// <summary>
    /// 分页
    /// </summary>
    /// <typeparam name="TSource"></typeparam>
    /// <param name="source"></param>
    /// <param name="pageAction"></param>
    /// <param name="pageSize"></param>
    public static void PagingProcess<TSource>(this IEnumerable<TSource> source, Action<IEnumerable<TSource>> pageAction, int pageSize)
    {
        if (source == null)
        {
            throw new ArgumentNullException("source");
        }
        if (pageAction == null)
        {
            throw new ArgumentNullException("pageAction");
        }
        if (pageSize <= 0)
        {
            throw new ArgumentOutOfRangeException("source", "页大小必须大于0");
        }
        int num = source.Count();
        if (num != 0)
        {
            for (int i = 0; i * pageSize < num; i++)
            {
                IEnumerable<TSource> obj = source.Skip(i * pageSize).Take(pageSize);
                pageAction(obj);
            }
        }
    }

    /// <summary>
    /// 分页
    /// </summary>
    /// <typeparam name="TSource"></typeparam>
    /// <param name="source"></param>
    /// <param name="pageAction"></param>
    /// <param name="pageSize"></param>
    public static void PagingProcess<TSource>(this IEnumerable<TSource> source, Action<int, IEnumerable<TSource>> pageAction, int pageSize)
    {
        if (source == null)
        {
            throw new ArgumentNullException("source");
        }
        if (pageAction == null)
        {
            throw new ArgumentNullException("pageAction");
        }
        if (pageSize <= 0)
        {
            throw new ArgumentOutOfRangeException("source", "页大小必须大于0");
        }
        int num = source.Count();
        if (num != 0)
        {
            for (int i = 0; i * pageSize < num; i++)
            {
                IEnumerable<TSource> arg = source.Skip(i * pageSize).Take(pageSize);
                pageAction(i, arg);
            }
        }
    }

    /// <summary>
    /// 分页
    /// </summary>
    /// <typeparam name="TSource"></typeparam>
    /// <param name="source"></param>
    /// <param name="pageAction"></param>
    /// <param name="pageSize"></param>
    public static IEnumerable<TTarget> PagingProcess<TSource, TTarget>(this IEnumerable<TSource> source, Func<IEnumerable<TSource>, IEnumerable<TTarget>> pageFunc, int pageSize)
    {
        if (source == null)
        {
            throw new ArgumentNullException("source");
        }
        if (pageFunc == null)
        {
            throw new ArgumentNullException("pageFunc");
        }
        if (pageSize <= 0)
        {
            throw new ArgumentOutOfRangeException("source", "页大小必须大于0");
        }
        int num = source.Count();
        if (num == 0)
        {
            return new List<TTarget>();
        }
        int i = 0;
        List<TTarget> list = new List<TTarget>();
        for (; i * pageSize < num; i++)
        {
            IEnumerable<TSource> arg = source.Skip(i * pageSize).Take(pageSize);
            list.AddRange(pageFunc(arg));
        }
        return list;
    }

    /// <summary>
    /// 分页
    /// </summary>
    /// <typeparam name="TSource"></typeparam>
    /// <param name="source"></param>
    /// <param name="pageAction"></param>
    /// <param name="pageSize"></param>
    public static IEnumerable<TTarget> PagingProcess<TSource, TTarget>(this IEnumerable<TSource> source, Func<int, IEnumerable<TSource>, IEnumerable<TTarget>> pageFunc, int pageSize)
    {
        if (source == null)
        {
            throw new ArgumentNullException("source");
        }
        if (pageFunc == null)
        {
            throw new ArgumentNullException("pageFunc");
        }
        if (pageSize <= 0)
        {
            throw new ArgumentOutOfRangeException("source", "页大小必须大于0");
        }
        int num = source.Count();
        if (num == 0)
        {
            return new List<TTarget>();
        }
        int i = 0;
        List<TTarget> list = new List<TTarget>();
        for (; i * pageSize < num; i++)
        {
            IEnumerable<TSource> arg = source.Skip(i * pageSize).Take(pageSize);
            list.AddRange(pageFunc(i, arg));
        }
        return list;
    }

    /// <summary>
    /// 并行分页
    /// </summary>
    /// <typeparam name="TSource"></typeparam>
    /// <param name="source"></param>
    /// <param name="pageAction"></param>
    /// <param name="pageSize"></param>
    public static IEnumerable<TTarget> ParallelPagingProcess<TSource, TTarget>(this IEnumerable<TSource> source, Func<IEnumerable<TSource>, IEnumerable<TTarget>> pageFunc, int pageSize, ParallelOptions parallelOptions = null)
    {
        if (source == null)
        {
            throw new ArgumentNullException("source");
        }
        if (pageFunc == null)
        {
            throw new ArgumentNullException("pageFunc");
        }
        if (pageSize <= 0)
        {
            throw new ArgumentOutOfRangeException("source", "页大小必须大于0");
        }
        int num = source.Count();
        if (num == 0)
        {
            return new List<TTarget>();
        }
        object _sync_lock = new object();
        int toExclusive = (int)Math.Ceiling((double)num / (double)pageSize) - 1;
        ParallelOptions parallelOptions2 = parallelOptions ?? new ParallelOptions
        {
            MaxDegreeOfParallelism = Environment.ProcessorCount
        };
        List<TTarget> data = new List<TTarget>();
        Parallel.For(0, toExclusive, parallelOptions2, () => new List<TTarget>(), delegate (int pageIndex, ParallelLoopState loopstate, List<TTarget> localStorage)
        {
            IEnumerable<TSource> enumerable = source.Skip(pageIndex * pageSize).Take(pageSize);
            if (enumerable.Any())
            {
                localStorage.AddRange(pageFunc(enumerable));
            }
            return localStorage;
        }, delegate (List<TTarget> finalStorage)
        {
            lock (_sync_lock)
            {
                data.AddRange(finalStorage);
            }
        });
        return data;
    }

    /// <summary>
    /// 分页
    /// </summary>
    /// <typeparam name="TSource"></typeparam>
    /// <param name="source"></param>
    /// <param name="pageAction"></param>
    /// <param name="pageSize"></param>
    public static async Task PagingProcessAsync<TSource>(this IEnumerable<TSource> source, Action<IEnumerable<TSource>> pageAction, int pageSize)
    {
        await source.PagingProcessAsync(pageAction, pageSize, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default);
    }

    /// <summary>
    /// 分页
    /// </summary>
    /// <typeparam name="TSource"></typeparam>
    /// <param name="source"></param>
    /// <param name="pageAction"></param>
    /// <param name="pageSize"></param>
    public static async Task PagingProcessAsync<TSource>(this IEnumerable<TSource> source, Action<IEnumerable<TSource>> pageAction, int pageSize, CancellationToken cancellationToken)
    {
        await source.PagingProcessAsync(pageAction, pageSize, cancellationToken, TaskCreationOptions.None, TaskScheduler.Default);
    }

    /// <summary>
    /// 分页
    /// </summary>
    /// <typeparam name="TSource"></typeparam>
    /// <param name="source"></param>
    /// <param name="pageAction"></param>
    /// <param name="pageSize"></param>
    public static async Task PagingProcessAsync<TSource>(this IEnumerable<TSource> source, Action<IEnumerable<TSource>> pageAction, int pageSize, CancellationToken cancellationToken, TaskCreationOptions creationOptions, TaskScheduler scheduler)
    {
        if (source == null)
        {
            throw new ArgumentNullException("source");
        }
        if (pageAction == null)
        {
            throw new ArgumentNullException("pageAction");
        }
        if (pageSize <= 0)
        {
            throw new ArgumentOutOfRangeException("source", "页大小必须大于0");
        }
        int count = source.Count();
        if (count == 0)
        {
            await Task.CompletedTask;
        }
        List<Task> list = new List<Task>();
        for (int i = 0; i * pageSize < count; i++)
        {
            IEnumerable<TSource> final = source.Skip(i * pageSize).Take(pageSize);
            Task item = Task.Factory.StartNew(delegate
            {
                pageAction(final);
            }, cancellationToken, creationOptions, scheduler);
            list.Add(item);
        }
        await Task.WhenAll(list).ConfigureAwait(continueOnCapturedContext: false);
    }

    /// <summary>
    /// 分页
    /// </summary>
    /// <typeparam name="TSource"></typeparam>
    /// <param name="source"></param>
    /// <param name="pageAction"></param>
    /// <param name="pageSize"></param>
    public static async Task PagingProcessAsync<TSource>(this IEnumerable<TSource> source, Action<int, IEnumerable<TSource>> pageAction, int pageSize)
    {
        await source.PagingProcessAsync(pageAction, pageSize, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default);
    }

    /// <summary>
    /// 分页
    /// </summary>
    /// <typeparam name="TSource"></typeparam>
    /// <param name="source"></param>
    /// <param name="pageAction"></param>
    /// <param name="pageSize"></param>
    public static async Task PagingProcessAsync<TSource>(this IEnumerable<TSource> source, Action<int, IEnumerable<TSource>> pageAction, int pageSize, CancellationToken cancellationToken)
    {
        await source.PagingProcessAsync(pageAction, pageSize, cancellationToken, TaskCreationOptions.None, TaskScheduler.Default);
    }

    /// <summary>
    /// 分页
    /// </summary>
    /// <typeparam name="TSource"></typeparam>
    /// <param name="source"></param>
    /// <param name="pageAction"></param>
    /// <param name="pageSize"></param>
    public static async Task PagingProcessAsync<TSource>(this IEnumerable<TSource> source, Action<int, IEnumerable<TSource>> pageAction, int pageSize, CancellationToken cancellationToken, TaskCreationOptions creationOptions, TaskScheduler scheduler)
    {
        if (source == null)
        {
            throw new ArgumentNullException("source");
        }
        if (pageAction == null)
        {
            throw new ArgumentNullException("pageAction");
        }
        if (pageSize <= 0)
        {
            throw new ArgumentOutOfRangeException("source", "页大小必须大于0");
        }
        int count = source.Count();
        if (count == 0)
        {
            await Task.CompletedTask;
        }
        List<Task> list = new List<Task>();
        for (int i = 0; i * pageSize < count; i++)
        {
            IEnumerable<TSource> final = source.Skip(i * pageSize).Take(pageSize);
            int tempPageIndex = i;
            Task item = Task.Factory.StartNew(delegate
            {
                pageAction(tempPageIndex, final);
            }, cancellationToken, creationOptions, scheduler);
            list.Add(item);
        }
        await Task.WhenAll(list).ConfigureAwait(continueOnCapturedContext: false);
    }

    /// <summary>
    /// 分页
    /// </summary>
    /// <typeparam name="TSource"></typeparam>
    /// <param name="source"></param>
    /// <param name="pageAction"></param>
    /// <param name="pageSize"></param>
    public static async Task<IEnumerable<TTarget>> PagingProcessAsync<TSource, TTarget>(this IEnumerable<TSource> source, Func<IEnumerable<TSource>, IEnumerable<TTarget>> pageFunc, int pageSize)
    {
        return await source.PagingProcessAsync(pageFunc, pageSize, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default);
    }

    /// <summary>
    /// 分页
    /// </summary>
    /// <typeparam name="TSource"></typeparam>
    /// <param name="source"></param>
    /// <param name="pageAction"></param>
    /// <param name="pageSize"></param>
    public static async Task<IEnumerable<TTarget>> PagingProcessAsync<TSource, TTarget>(this IEnumerable<TSource> source, Func<IEnumerable<TSource>, IEnumerable<TTarget>> pageFunc, int pageSize, CancellationToken cancellationToken)
    {
        return await source.PagingProcessAsync(pageFunc, pageSize, cancellationToken, TaskCreationOptions.None, TaskScheduler.Default);
    }

    /// <summary>
    /// 分页
    /// </summary>
    /// <typeparam name="TSource"></typeparam>
    /// <param name="source"></param>
    /// <param name="pageAction"></param>
    /// <param name="pageSize"></param>
    public static async Task<IEnumerable<TTarget>> PagingProcessAsync<TSource, TTarget>(this IEnumerable<TSource> source, Func<IEnumerable<TSource>, IEnumerable<TTarget>> pageFunc, int pageSize, CancellationToken cancellationToken, TaskCreationOptions creationOptions, TaskScheduler scheduler)
    {
        if (source == null)
        {
            throw new ArgumentNullException("source");
        }
        if (pageFunc == null)
        {
            throw new ArgumentNullException("pageFunc");
        }
        if (pageSize <= 0)
        {
            throw new ArgumentOutOfRangeException("source", "页大小必须大于0");
        }
        int count = source.Count();
        if (count == 0)
        {
            await Task.CompletedTask;
        }
        List<Task<IEnumerable<TTarget>>> list = new List<Task<IEnumerable<TTarget>>>();
        for (int i = 0; i * pageSize < count; i++)
        {
            IEnumerable<TSource> final = source.Skip(i * pageSize).Take(pageSize);
            Task<IEnumerable<TTarget>> item = Task.Factory.StartNew(() => pageFunc(final), cancellationToken, creationOptions, scheduler);
            list.Add(item);
        }
        return (await Task.WhenAll(list).ConfigureAwait(continueOnCapturedContext: false)).SelectMany((IEnumerable<TTarget> x) => x);
    }

    /// <summary>
    /// 分页
    /// </summary>
    /// <typeparam name="TSource"></typeparam>
    /// <param name="source"></param>
    /// <param name="pageAction"></param>
    /// <param name="pageSize"></param>
    public static async Task<IEnumerable<TTarget>> PagingProcessAsync<TSource, TTarget>(this IEnumerable<TSource> source, Func<int, IEnumerable<TSource>, IEnumerable<TTarget>> pageFunc, int pageSize)
    {
        return await source.PagingProcessAsync(pageFunc, pageSize, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default);
    }

    /// <summary>
    /// 分页
    /// </summary>
    /// <typeparam name="TSource"></typeparam>
    /// <param name="source"></param>
    /// <param name="pageAction"></param>
    /// <param name="pageSize"></param>
    public static async Task<IEnumerable<TTarget>> PagingProcessAsync<TSource, TTarget>(this IEnumerable<TSource> source, Func<int, IEnumerable<TSource>, IEnumerable<TTarget>> pageFunc, int pageSize, CancellationToken cancellationToken)
    {
        return await source.PagingProcessAsync(pageFunc, pageSize, cancellationToken, TaskCreationOptions.None, TaskScheduler.Default);
    }

    /// <summary>
    /// 分页
    /// </summary>
    /// <typeparam name="TSource"></typeparam>
    /// <param name="source"></param>
    /// <param name="pageAction"></param>
    /// <param name="pageSize"></param>
    public static async Task<IEnumerable<TTarget>> PagingProcessAsync<TSource, TTarget>(this IEnumerable<TSource> source, Func<int, IEnumerable<TSource>, IEnumerable<TTarget>> pageFunc, int pageSize, CancellationToken cancellationToken, TaskCreationOptions creationOptions, TaskScheduler scheduler)
    {
        if (source == null)
        {
            throw new ArgumentNullException("source");
        }
        if (pageFunc == null)
        {
            throw new ArgumentNullException("pageFunc");
        }
        if (pageSize <= 0)
        {
            throw new ArgumentOutOfRangeException("source", "页大小必须大于0");
        }
        int count = source.Count();
        if (count == 0)
        {
            await Task.CompletedTask;
        }
        List<Task<IEnumerable<TTarget>>> list = new List<Task<IEnumerable<TTarget>>>();
        for (int i = 0; i * pageSize < count; i++)
        {
            IEnumerable<TSource> final = source.Skip(i * pageSize).Take(pageSize);
            int tempPageIndex = i;
            Task<IEnumerable<TTarget>> item = Task.Factory.StartNew(() => pageFunc(tempPageIndex, final), cancellationToken, creationOptions, scheduler);
            list.Add(item);
        }
        return (await Task.WhenAll(list).ConfigureAwait(continueOnCapturedContext: false)).SelectMany((IEnumerable<TTarget> x) => x);
    }
}
